package com.zaicheng.cloud.processor;

import com.zaicheng.cloud.RemoteCallsRegistrar;
import com.zaicheng.cloud.annotation.ConditionEvaluator;
import com.zaicheng.cloud.annotation.RemoteCallClient;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;

import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;

/**
 @author zaicheng
 @version 1.0
 @date 2022-04-19
 @email zzc15847256473@163.com
 */

public class ClassPathScanningCandidateComponent {

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    private  final List<String> fileNames = new ArrayList<>();

    private String resourcePattern = DEFAULT_RESOURCE_PATTERN;

    LinkedHashSet<BeanDefinition> candidates = new LinkedHashSet<>();

    private final BeanDefinitionRegistry registry;

    @Nullable
    private ResourcePatternResolver resourcePatternResolver;

    @Nullable
    private MetadataReaderFactory metadataReaderFactory;

    private final List<TypeFilter> includeFilters = new LinkedList<>();

    private final List<TypeFilter> excludeFilters = new LinkedList<>();

    @Nullable
    private ConditionEvaluator conditionEvaluator;

    @Nullable
    private Environment environment;

    public ClassPathScanningCandidateComponent(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    public Set<BeanDefinition>  findCandidateComponents(String packageName) throws IOException {

        // 传入包路径将.替换成/
        String path = packageName.replace(".", "/");

        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                path + '/' + this.resourcePattern;

        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

        for (Resource resource:resources){
            if (resource.isReadable()){
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    if (isCandidateComponent(metadataReader)){
                        ScannedGenericBeanDefinition sbd =new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        // 这里占时忽略掉，等后续补充
//                        if (isCandidateComponent(sbd)){
                            candidates.add(sbd);
//                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return  candidates;
    }



    private ResourcePatternResolver getResourcePatternResolver() {
        if (this.resourcePatternResolver == null) {
            this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
        }
        return this.resourcePatternResolver;
    }


    public final MetadataReaderFactory getMetadataReaderFactory() {
        if (this.metadataReaderFactory == null) {
            this.metadataReaderFactory = new CachingMetadataReaderFactory();
        }
        return this.metadataReaderFactory;
    }


    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }


    private boolean isConditionMatch(MetadataReader metadataReader) {
        if (this.conditionEvaluator == null) {
            this.conditionEvaluator =
                    new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
        }
        return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
    }


    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return (metadata.isIndependent() && (metadata.isConcrete() ||
                (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
    }

    public final BeanDefinitionRegistry getRegistry() {
        return this.registry;
    }

    public void setEnvironment(Environment environment) {
        Assert.notNull(environment, "Environment must not be null");
        this.environment = environment;
        this.conditionEvaluator = null;
    }

    public void addIncludeFilter(TypeFilter includeFilter) {
        this.includeFilters.add(includeFilter);
    }

    public final Environment getEnvironment() {
        if (this.environment == null) {
            this.environment = new StandardEnvironment();
        }
        return this.environment;
    }

    // 递归获取类的绝对路径
    public  void  getFileInfo(String url){
        File file = new File(url);
//        System.out.println("文件信息："+file);
        // 判断文件是不是一个目录
        if (file.isDirectory()){
            // 拿到我们目录的全部文件
            File[] files = file.listFiles();
            for (File file1:files) {
                //拿到我们文件的绝对路径
                if (file1.isDirectory()){
                    String absolutePath = file1.getAbsolutePath();
//                    System.out.println("绝对路径：" + absolutePath);
                    getFileInfo(absolutePath);
                }else{
                    fileNames.add(file1.getAbsolutePath());
                }
            }
        }
    }

}
